import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * package PACKAGE_NAME
 * Created  on 2020/5/14.
 */
public class MainGUI {

    private final GTerm gt;
    private final int textFieldId;
    private final int listIndex;
    private int currentPowerPosition;
    private Device[] devices;
    private int currentNumDevices;

    /**
     * Constructor func. init a MainGUI object which runs the main window.
     */
    public MainGUI() {
        // init attributes
        this.currentPowerPosition = 0;
        this.devices = new Device[1];
        this.currentNumDevices = 0;

        // init main gt window.
        this.gt = new GTerm(450, 310);
        // init title
        this.gt.setTitle("OffGridManager v1.00");
        // init text field label
        this.gt.print("Current power position (Watts): ");
        // init text field
        this.gt.addTextField("0", 150);
        this.textFieldId = 0;

        // add "Add Device" button, set listen handler func .
        this.gt.setXY(0, 20);
        this.gt.addButton("Add Device", this, "addDevice");
        // add "Remove Device" button, set listen handler func .
        this.gt.addButton("Remove Device", this, "removeDevice");
        // add toggle button
        this.gt.addButton("Toggle on/off", this, "toggleStatus");
        // add list, set listen handler func evaluateSelectedCalculation.
        this.gt.setXY(0, 60);
        this.gt.addList(400, 240, this, "deviceProcess");
        this.listIndex = 0;// the list object index
    }

    /**
     * main function as entry
     *
     * @param args main function args
     */
    public static void main(String[] args) {
        new MainGUI();
    }

    /**
     * add device button handler function
     */
    public void addDevice() {
        try {
            this.gt.showMessageDialog("Select image for device");
            // get filepath
            String imageFilePath = this.gt.getFilePath();
            // get name only
            Path path = Paths.get(imageFilePath);
            String deviceName = path.getFileName().toString().replaceFirst("[.][^.]+$", "");

            // get power consumption
            String Consumption = this.gt.getInputString(String.format("Enter power consumption (negative for production) in Watts for %s", deviceName));
            int powerConsumption = Integer.parseInt(Consumption);

            // init a new device, construct from filename,image, watt and etc;
            Device device = new Device(imageFilePath, deviceName, powerConsumption);
            this.gt.addElementToList(this.listIndex, device);

            // add device to self maintained array devices
            if (this.currentNumDevices >= this.devices.length)
                expandArray();
            this.devices[this.currentNumDevices] = device;
            this.currentNumDevices += 1;

            // sum current power position
            sumPowerPosition();
        } catch (NumberFormatException e) {
            this.gt.showMessageDialog("cannot parse input power consumption!");
        } catch (NullPointerException e) {
            this.gt.showMessageDialog("Problem: Something that was meant to be there was not there!");
        } catch (Exception e) {
            this.gt.showMessageDialog("Problem: Something else went wrong!");
        }


    }

    /**
     * remove device button handler function
     */
    public void removeDevice() {
        // logic to remove device
        // 1. first need to check one list element is selected
        try {
            Device device = (Device) this.gt.getSelectedElementFromList(this.listIndex);
            // to remove device, the device must be turned off first.
            if (device.getStatus()) {
                Boolean tmpStatus = device.getStatus();
                device.setStatus(false);
                int tmpSum = this.sumPowerPosition();
                // if sum power <0 ,the device cannot change status. rollback the device status
                if (tmpSum < 0) {
                    // rollback status
                    device.setStatus(tmpStatus);
                    this.ShowCantToggleMessageByStatus(device);
                    this.gt.showMessageDialog("Unable to remove device");
                }
                // else the device can be turned off, delete the device and update the power position
                else {
                    int listElementIndex = getElementIndex(device);
                    deleteDevice(device, listElementIndex);
                    this.currentPowerPosition = tmpSum;
                    this.showPowerPosition();
                }

            }
            // else the device is already turned off, execute deletion
            else {
                //find the selected element index, remove from list and devices array.
                int listElementIndex = getElementIndex(device);
                deleteDevice(device, listElementIndex);
            }

        } catch (NullPointerException e) {
            this.gt.showMessageDialog("You must select one device in the list!");
        } catch (Exception e) {
            this.gt.showMessageDialog("Problem: Something else went wrong!");
        }
    }

    /**
     * the toggle on/off handler function
     */
    public void toggleStatus() {
        //logic to toggle Status
        // 1. first need to check one list element is selected
        try {
            Device device = (Device) this.gt.getSelectedElementFromList(this.listIndex);
            // switch device status
            boolean tmpStatus = device.getStatus();
            device.setStatus(!tmpStatus);
            int tmpSum = this.sumPowerPosition();
            // if sum<0 ,the device cannot change status. rollback the device status
            if (tmpSum < 0) {
                // rollback status
                device.setStatus(tmpStatus);
                this.ShowCantToggleMessageByStatus(device);
            }
            // the device toggle on/off, cal the power position, refresh render the device.
            else {
                this.currentPowerPosition = tmpSum;
                this.showPowerPosition();
                device.renderOutput();
            }

        } catch (NullPointerException e) {
            this.gt.showMessageDialog("No device is selected!");
        } catch (Exception e) {
            this.gt.showMessageDialog("Problem: Something else went wrong!");
        }
    }

    private void expandArray() {
        Device[] biggerDevices = new Device[this.currentNumDevices * 2];
        int i = 0;
        while (i < this.currentNumDevices) {
            biggerDevices[i] = this.devices[i];
            i += 1;
        }
        this.devices = biggerDevices;
    }

    private int sumPowerPosition() {
        int sum = 0;
        for (int i = 0; i < this.currentNumDevices; i++) {
            Device device = this.devices[i];
            if (device.getStatus())
                sum += (device.getPowerRating() * -1);
        }
        return sum;
    }

    private void showPowerPosition() {
        // output currentPowerPosition
        this.gt.setTextInEntry(this.textFieldId, Integer.toString(this.currentPowerPosition));
    }

    private int getElementIndex(Device device) {
        int index = -1;
        for (int i = 0; i < this.currentNumDevices; i++) {
            if (devices[i] == device) {
                index = i;
                break;
            }
        }
        return index;
    }

    private void deleteDevice(Device device, int listElementIndex) {
        // remove element from list
        this.gt.removeElementFromList(this.listIndex, listElementIndex);
        // remove element from array devices,shift the later elements forward
        for (int i = 0; i < this.currentNumDevices; i++) {
            if (devices[i] == device) {
                // shifting elements
                for (int j = i; j < this.currentNumDevices - 1; j++) {
                    devices[j] = devices[j + 1];
                }
                break;
            }
        }

        this.currentNumDevices = this.currentNumDevices - 1;
        // close the device window
        device.getGt().close();
    }

    /**
     * Show the message box when toggle button is pressed AND the device cannot be turned on/off
     *
     * @param device the device param for check status
     */
    private void ShowCantToggleMessageByStatus(Device device) {
        String message;
        // if device WAS on,now cannot turn off
        if (device.getStatus()) {
            message = String.format("Not enough power to turn off %s", device.getName());
        } else // if device WAS off, now cannot turn off
        {
            message = String.format("Not enough power to turn on %s", device.getName());
        }
        this.gt.showMessageDialog(message);
    }

    /**
     * process click event handler function. no function for now
     */
    public void deviceProcess() {
    }
}
